home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 422_02 / misc / basic.c < prev    next >
C/C++ Source or Header  |  1994-03-20  |  21KB  |  916 lines

  1. /*
  2.  * MICRO BASIC:
  3.  *
  4.  * This is a very simple INTEGER BASIC interpreter that I wrote a number of
  5.  * years ago, and subsequently ported to MICRO-C. While not the greatest
  6.  * example of coding style (it was a quick and dirty hack job), It is quite
  7.  * instructive, as a simple but fairly complete interpreter.
  8.  *
  9.  * Variables:
  10.  *     260 Numeric   variables: A0-A9 ... Z0-Z9
  11.  *     260 Character variables: A0$-A9$ ... Z0$-Z9$
  12.  * 
  13.  *     NOTE: For convenience the '0' variables can be referenced by letter
  14.  *           only. IE: A is equivalent to A0 ... Z$ is equivalent to Z0$
  15.  * 
  16.  * Statements:
  17.  *     LET (default)             - variable = expression
  18.  *     EXIT                    - Terminate MICRO-BASIC
  19.  *     LIST [start,[end]]        - List program lines
  20.  *     LIST#n ...                - List program to file (0-9)
  21.  *     NEW                        - Erase program and variables
  22.  *     RUN [line]                - Run program
  23.  *     CLEAR                    - Erase variables only
  24.  *     GOSUB line                - Call a subroutine
  25.  *     GOTO  line                - Jump to line
  26.  *     RETURN                    - Return from subroutine
  27.  *     PRINT expr[,expr ...]    - Print to console
  28.  *     PRINT#n ...                - Print to file (0-9)
  29.  *     FOR v=init TO limit [STEP increment] - Perform a counted loop
  30.  *     NEXT [v]                - End counted loop
  31.  *     IF test THEN line        - Conditional goto
  32.  *     IF test THEN statement    - Conditional statement (next statement only)
  33.  *     LIF test THEN statements- LONG IF (all statements to end of line)
  34.  *     REM                        - Comment... reminder of line is ignored
  35.  *     STOP                    - Terminate program & issue message
  36.  *     END                        - Terminate program with no message
  37.  *     INPUT var                - Get value for variable
  38.  *     INPUT "prompt",var        - Get value of variable with prompt
  39.  *       NOTE:    prompt must be a constant string, however you can use
  40.  *             a char variable in prompt by concatinating it to such
  41.  *             a string: INPUT ""+a$,b$
  42.  *     INPUT#n,var                - Get value for variable from file (0-9)
  43.  *     OPEN#n,"name","opts"    - Open file (0-9), opts are same as "fopen()"
  44.  *     CLOSE#n                    - Close file (0-9)
  45.  * 
  46.  * Operators:
  47.  *     +                        - Addition, string concatination
  48.  *     -                        - Unary minus, subtraction
  49.  *     *, /, %,                - multiplication, division, modulus
  50.  *     &, |, ^                    - AND, OR, Exclusive OR
  51.  *     =, <>                    - Assignment/test equal, test NOTequal (num or string)
  52.  *     <, <=, >, >=            - LT, LE, GT, GE (numbers only)
  53.  *     !                        - Unary NOT
  54.  * 
  55.  * Functions:
  56.  *     CHR$(value)                - Returns character of passed value
  57.  *     STR$(value)                - Returns ASCII string of value's digits
  58.  *     ASC(char)                - Returns value of passed character
  59.  *     ABS(value)                - Returns absolute value of argument
  60.  *
  61.  * If want to use a different compiler, take note:
  62.  *
  63.  *    -    Make sure 'fgets' does not include the trailing NEWLINE
  64.  *        You can use "microc.h" from utilities source directory.
  65.  *
  66.  *    -    Make sure that 'isalpha' and 'isdigit' can deal with negative
  67.  *        character values (most macro implementations can't). If not,
  68.  *        include those functions from the MICRO-C library.
  69.  *
  70.  *    -    Modify the declaration of 'savjmp' to whatever is appropriate
  71.  *        for your compilers 'setjmp' and 'longjmp' functions.
  72.  *
  73.  * Copyright 1982-1994 Dave Dunfield
  74.  * All rights reserved.
  75.  *
  76.  * Permission granted for personal (non-commercial) use only.
  77.  *
  78.  * Compile command: cc basic -fop
  79.  */
  80. #include <stdio.h>
  81.  
  82. /* Fixed parameters */
  83. #define BUFFER_SIZE 100        /* input buffer size */
  84. #define NUM_VAR 260            /* number of variables */
  85. #define SA_SIZE 100            /* string accumulator size */
  86.  
  87. /* Reserved word grouping */
  88. #define SEC 21                /* secondary keywords */
  89. #define OPS SEC+3            /* first operator */
  90. #define SOPS OPS+14            /* first string function */
  91. #define NOPS SOPS+2            /* first numeric function */
  92.  
  93. /* Control stack constant identifiers */
  94. #define FOR 1000            /* indicate FOR statement */
  95. #define GOSUB FOR+1            /* indicate GOSUB statement */
  96.  
  97. struct line_record {
  98.     unsigned Lnumber;
  99.     struct line_record *Llink;
  100.     char Ltext[]; };
  101.  
  102. static char *reserved_words[] = {
  103.     "LET", "EXIT", "LIST", "NEW", "RUN", "CLEAR", "GOSUB", "GOTO",
  104.     "RETURN", "PRINT", "FOR", "NEXT", "IF", "LIF", "REM", "STOP",
  105.     "END", "INPUT", "OPEN", "CLOSE", "TO", "STEP", "THEN",
  106.     "+", "-", "*", "/", "%", "&", "|", "^",
  107.     "=", "<>", "<=", "<", ">=", ">",
  108.     "CHR$(", "STR$(", "ASC(", "ABS(",
  109.     0 };
  110.  
  111. static char priority[] = {
  112.     0, 1, 1, 2, 2, 2, 3, 3, 3,
  113.     1, 1, 1, 1, 1, 1
  114.     };
  115.  
  116. static char *error_messages[] = {
  117.     "Syntax",
  118.     "Illegal program",
  119.     "Illegal direct",
  120.     "Line number",
  121.     "Wrong type",
  122.     "Divide by zero",
  123.     "Nesting",
  124.     "File not open",
  125.     "File already open",
  126.     "Input"
  127.     };
  128.  
  129. char sa1[SA_SIZE], sa2[SA_SIZE];        /* string accumulators */
  130. struct line_record *pgm_start, *runptr;    /* Line tracking pointers */
  131.  
  132. int num_vars[NUM_VAR] = { 0 };            /* Numeric variables */
  133. char *char_vars[NUM_VAR] = { 0 };        /* Character variables */
  134.  
  135. FILE *files[10]={0}, *filein, *fileout;    /* File unit numbers */
  136.  
  137. int savjmp[3];                            /* Save area for set/longjmp */
  138.  
  139. /* Misc. global variables */
  140. char *cmdptr, buffer[BUFFER_SIZE], mode,  expr_type, nest;
  141. unsigned line, ctl_ptr = 0, ctl_stk[100];
  142.  
  143. /* test for end of expression */
  144. isend(c)
  145.     char c;
  146. {
  147.     if((c >= (0xff80+SEC)) && (c < (0xff80+OPS)))
  148.         return(1);
  149.     return (c == '\0') || (c == ':') || (c == ')') || (c == ',');
  150. }
  151.  
  152. /* test for end of statement */
  153. islend(c)
  154.     char c;
  155. {
  156.     return (c == '\0') || (c == ':');
  157. }
  158.  
  159. /* test for terminator character */
  160. isterm(c)
  161.     char c;
  162. {
  163.     return (c == ' ') || (c == '\t');
  164. }
  165.  
  166. /* advance to next non-blank */
  167. char skip_blank()
  168. {
  169.     while(isterm(*cmdptr))
  170.         ++cmdptr;
  171.     return *cmdptr;
  172. }
  173.  
  174. /* advance to., return and skip next non blank */
  175. char skip_next()
  176. {
  177.     char c;
  178.  
  179.     while(isterm(c=*cmdptr))
  180.         ++cmdptr;
  181.     if(c)
  182.         ++cmdptr;
  183.     return c;
  184. }
  185.  
  186. /* translate to special codes */
  187. translate()
  188. {
  189.     unsigned value;
  190.     char *ptr, c;
  191.  
  192.     cmdptr = ptr = buffer;
  193.  
  194.     while(c = *cmdptr) {
  195.         if(value = lookup(reserved_words))
  196.             *ptr++ = value + 0x80;
  197.         else {
  198.             *ptr++ = c;
  199.             ++cmdptr;
  200.             if(c == '"') {        /* double quote */
  201.                 while((c = *cmdptr) && (c != '"')) {
  202.                     ++cmdptr;
  203.                     *ptr++ = c; }
  204.                 *ptr++ = *cmdptr++; } } }
  205.     *ptr = 0;
  206.     cmdptr = buffer;
  207. }
  208.  
  209. /* prompt for and get a line from standard input */
  210. char get_line(prompt)
  211.     char *prompt;
  212. {
  213.     fputs(prompt, stdout);
  214.     fgets(buffer, BUFFER_SIZE, stdin);
  215.     translate();
  216.     return skip_blank();
  217. }
  218.  
  219. /* get a number from the input buffer */
  220. get_num()
  221. {
  222.     unsigned value;
  223.     char c;
  224.  
  225.     value = 0;
  226.     while(isdigit(c=*cmdptr)) {
  227.         ++cmdptr;
  228.         value = (value * 10) + (c - '0'); }
  229.     return value;
  230. }
  231.  
  232. /* lookup up word from command line in table */
  233. lookup(table)
  234.     char *table[];
  235. {
  236.     unsigned i;
  237.     char *cptr, *optr;
  238.  
  239.     optr = cmdptr;
  240.     for(i=0; cptr = table[i]; ++i) {
  241.         while((*cptr) && (*cptr == toupper(*cmdptr))) {
  242.             ++cptr;
  243.             ++cmdptr; }
  244.         if(!*cptr) {
  245.             skip_blank();
  246.             return i+1; }
  247.         cmdptr = optr; }
  248.     return 0;
  249. }
  250.  
  251. /* main program */
  252. main()
  253. {
  254.     unsigned value;
  255.     char cmd;
  256.  
  257.     pgm_start = 0;
  258.     setjmp(savjmp);
  259.     for(;;) {        /* main command loop */
  260.         mode = ctl_ptr = 0;
  261.         while(!get_line("Ready\n"));    /* insure we get command */
  262.         if(0 > (cmd = skip_blank())) {
  263.             ++cmdptr;
  264.             execute(cmd); }
  265.         else {                                /* not a known command */
  266.             if(isdigit(*cmdptr)) {            /* editing... */
  267.                 value = get_num();            /* get line number */
  268.                 delete_line(value);            /* delete the old */
  269.                 if(skip_blank())
  270.                     insert_line(value); }    /* insert the new */
  271.             else
  272.                 execute(1); } }                /* assume let */
  273. }
  274.  
  275. /* delete a line from the program */
  276. delete_line(lino)
  277.     unsigned lino;
  278. {
  279.     struct line_record *cptr, *bptr;
  280.  
  281.     if(!(cptr = pgm_start))                    /* no lines in pgm */
  282.         return;
  283.     do {
  284.         if(lino == cptr->Lnumber) {            /* we have line to delete */
  285.             if(cptr == pgm_start) {            /* first line in pgm */    
  286.                 pgm_start = cptr->Llink;
  287.                 return; }
  288.             else {
  289.                 bptr->Llink = cptr->Llink;    /* skip it in linked list */
  290.                 free(cptr); } }                /* let it go */
  291.         bptr = cptr; }
  292.     while(cptr = cptr->Llink);
  293. }
  294.  
  295. /* Insert a line into the program */
  296. insert_line(lino)
  297.     unsigned lino;
  298. {
  299.     unsigned i;
  300.     struct line_record *cptr, *bptr, *optr;
  301.     char *ptr;
  302.  
  303.     ptr = cmdptr;
  304.     for(i=5; *ptr; ++i)
  305.         ++ptr